/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package view;

import dom.Command;
import dom.workflow.RunMode;
import dom.workflow.WorkFlow;
import imp.command.ConcreteCommand;
import imp.reciever.FileControl;
import imp.reciever.RecieverFactory;
import imp.reciever.RecieverName;
import imp.reciever.input.ReadFolder;
import imp.reciever.output.SaveFiles;
import imp.reciever.process.Decrypt;
import imp.reciever.process.Encrypt;
import imp.reciever.process.ExecuteSH;
import imp.reciever.process.Zip;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFileChooser;

/**
 *
 * @author julian.pena
 */
public class MotorCommand extends javax.swing.JFrame {

    public static final String SELECT_AN_OPTION = "seleccione una opcion";
    private JFileChooser fc;
    private List<WorkFlow> workFlows;
    private List<Command> commands;

    /**
     * Creates new form MotorCommand
     */
    public MotorCommand() {
        initComponents();
        init();
    }

    /**
     * This method is called from within the constructor to initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is always
     * regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        runModeBG = new javax.swing.ButtonGroup();
        jLabel1 = new javax.swing.JLabel();
        actionCB = new javax.swing.JComboBox();
        jLabel2 = new javax.swing.JLabel();
        sourceTF = new javax.swing.JTextField();
        jLabel3 = new javax.swing.JLabel();
        destinationTF = new javax.swing.JTextField();
        addB = new javax.swing.JButton();
        jSeparator1 = new javax.swing.JSeparator();
        saveWorkFlowB = new javax.swing.JButton();
        messageL = new javax.swing.JLabel();
        actionsL = new java.awt.List();
        removeB = new javax.swing.JButton();
        jLabel4 = new javax.swing.JLabel();
        jLabel5 = new javax.swing.JLabel();
        nameTF = new javax.swing.JTextField();
        downB = new javax.swing.JButton();
        upB = new javax.swing.JButton();
        jLabel6 = new javax.swing.JLabel();
        sequencialRB = new javax.swing.JRadioButton();
        parallelRB = new javax.swing.JRadioButton();
        jSeparator2 = new javax.swing.JSeparator();
        workFlowsCB = new javax.swing.JComboBox();
        executeB = new javax.swing.JButton();
        mainCommandSP = new javax.swing.JSpinner();
        mainCommandL = new javax.swing.JLabel();

        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
        getContentPane().setLayout(new org.netbeans.lib.awtextra.AbsoluteLayout());

        jLabel1.setText("Seleccione una accion:");
        getContentPane().add(jLabel1, new org.netbeans.lib.awtextra.AbsoluteConstraints(20, 20, 170, -1));

        actionCB.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                actionCBActionPerformed(evt);
            }
        });
        getContentPane().add(actionCB, new org.netbeans.lib.awtextra.AbsoluteConstraints(200, 10, 150, -1));

        jLabel2.setText("Origen:");
        getContentPane().add(jLabel2, new org.netbeans.lib.awtextra.AbsoluteConstraints(20, 60, -1, -1));

        sourceTF.setEditable(false);
        sourceTF.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                sourceTFMouseClicked(evt);
            }
        });
        getContentPane().add(sourceTF, new org.netbeans.lib.awtextra.AbsoluteConstraints(90, 50, 260, -1));

        jLabel3.setText("Destino:");
        getContentPane().add(jLabel3, new org.netbeans.lib.awtextra.AbsoluteConstraints(20, 90, -1, -1));

        destinationTF.setEditable(false);
        destinationTF.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                destinationTFMouseClicked(evt);
            }
        });
        getContentPane().add(destinationTF, new org.netbeans.lib.awtextra.AbsoluteConstraints(90, 80, 260, -1));

        addB.setText("agregar");
        addB.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                addBActionPerformed(evt);
            }
        });
        getContentPane().add(addB, new org.netbeans.lib.awtextra.AbsoluteConstraints(290, 120, -1, -1));
        getContentPane().add(jSeparator1, new org.netbeans.lib.awtextra.AbsoluteConstraints(20, 160, 340, 10));

        saveWorkFlowB.setText("guardar");
        saveWorkFlowB.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                saveWorkFlowBActionPerformed(evt);
            }
        });
        getContentPane().add(saveWorkFlowB, new org.netbeans.lib.awtextra.AbsoluteConstraints(280, 330, 80, -1));
        getContentPane().add(messageL, new org.netbeans.lib.awtextra.AbsoluteConstraints(20, 480, 340, 30));

        actionsL.setBackground(java.awt.SystemColor.controlLtHighlight);
        getContentPane().add(actionsL, new org.netbeans.lib.awtextra.AbsoluteConstraints(20, 200, 150, 100));

        removeB.setText("quitar");
        removeB.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                removeBActionPerformed(evt);
            }
        });
        getContentPane().add(removeB, new org.netbeans.lib.awtextra.AbsoluteConstraints(290, 210, 70, -1));

        jLabel4.setText("Tareas");
        getContentPane().add(jLabel4, new org.netbeans.lib.awtextra.AbsoluteConstraints(20, 170, 100, -1));

        jLabel5.setText("nombre: ");
        getContentPane().add(jLabel5, new org.netbeans.lib.awtextra.AbsoluteConstraints(20, 320, -1, -1));

        nameTF.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                nameTFMouseClicked(evt);
            }
        });
        getContentPane().add(nameTF, new org.netbeans.lib.awtextra.AbsoluteConstraints(90, 310, 170, -1));

        downB.setText("-");
        downB.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                downBActionPerformed(evt);
            }
        });
        getContentPane().add(downB, new org.netbeans.lib.awtextra.AbsoluteConstraints(180, 240, 40, 30));

        upB.setText("+");
        upB.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                upBActionPerformed(evt);
            }
        });
        getContentPane().add(upB, new org.netbeans.lib.awtextra.AbsoluteConstraints(180, 200, 40, 30));

        jLabel6.setText("Modo :");
        getContentPane().add(jLabel6, new org.netbeans.lib.awtextra.AbsoluteConstraints(20, 350, 60, -1));

        sequencialRB.setText("sequiencial");
        sequencialRB.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                sequencialRBActionPerformed(evt);
            }
        });
        getContentPane().add(sequencialRB, new org.netbeans.lib.awtextra.AbsoluteConstraints(80, 350, -1, -1));

        parallelRB.setText("paralelo");
        parallelRB.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                parallelRBActionPerformed(evt);
            }
        });
        getContentPane().add(parallelRB, new org.netbeans.lib.awtextra.AbsoluteConstraints(190, 350, -1, -1));
        getContentPane().add(jSeparator2, new org.netbeans.lib.awtextra.AbsoluteConstraints(20, 420, 340, -1));

        getContentPane().add(workFlowsCB, new org.netbeans.lib.awtextra.AbsoluteConstraints(20, 430, 240, -1));

        executeB.setText("ejecutar");
        executeB.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                executeBActionPerformed(evt);
            }
        });
        getContentPane().add(executeB, new org.netbeans.lib.awtextra.AbsoluteConstraints(290, 430, -1, -1));

        mainCommandSP.setModel(new javax.swing.SpinnerNumberModel(Integer.valueOf(1), Integer.valueOf(1), null, Integer.valueOf(1)));
        mainCommandSP.addChangeListener(new javax.swing.event.ChangeListener() {
            public void stateChanged(javax.swing.event.ChangeEvent evt) {
                mainCommandSPStateChanged(evt);
            }
        });
        getContentPane().add(mainCommandSP, new org.netbeans.lib.awtextra.AbsoluteConstraints(230, 380, -1, -1));

        mainCommandL.setText("Comando principal:");
        getContentPane().add(mainCommandL, new org.netbeans.lib.awtextra.AbsoluteConstraints(79, 380, 140, 30));

        pack();
    }// </editor-fold>//GEN-END:initComponents
   
    private void actionCBActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_actionCBActionPerformed
        String action = actionCB.getSelectedItem().toString();
        if (action.equals(SELECT_AN_OPTION)) {
            sourceTF.setVisible(false);
            destinationTF.setVisible(false);
            addB.setEnabled(false);
            nameTF.setEnabled(false);
        } else if (action.equals(RecieverName.READ_FODER.getValue())) {
            sourceTF.setVisible(true);
            destinationTF.setVisible(false);
            addB.setEnabled(true);
            nameTF.setEnabled(true);
        } else if (action.equals(RecieverName.SAVE_FILES.getValue())) {
            sourceTF.setVisible(false);
            destinationTF.setVisible(true);
            addB.setEnabled(true);
            nameTF.setEnabled(true);
        } else if (action.equals(RecieverName.EXECUTE_SCRIPT.getValue())) {
            sourceTF.setVisible(true);
            destinationTF.setVisible(false);
            addB.setEnabled(true);
            nameTF.setEnabled(true);
        } else if (action.equals(RecieverName.ENCRYPT.getValue())) {
            sourceTF.setVisible(false);
            destinationTF.setVisible(false);
            addB.setEnabled(true);
            nameTF.setEnabled(true);
        } else if (action.equals(RecieverName.DECRYPT.getValue())) {
            sourceTF.setVisible(false);
            destinationTF.setVisible(false);
            addB.setEnabled(true);
            nameTF.setEnabled(true);
        } else if (action.equals(RecieverName.DECOMPRESS.getValue())) {
            sourceTF.setVisible(false);
            destinationTF.setVisible(false);
            addB.setEnabled(true);
            nameTF.setEnabled(true);
        } else if (action.equals(RecieverName.COMPRESS.getValue())) {
            sourceTF.setVisible(false);
            destinationTF.setVisible(false);
            addB.setEnabled(true);
            nameTF.setEnabled(true);
        } else {
            sourceTF.setVisible(false);
            destinationTF.setVisible(false);
            addB.setEnabled(true);
        }
        clearMessage();
        clearPaths();
    }//GEN-LAST:event_actionCBActionPerformed

    private void sourceTFMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_sourceTFMouseClicked
        int returnVal;
        if (actionCB.getSelectedItem().toString().equals(RecieverName.EXECUTE_SCRIPT.getValue())) {
            fc.setFileSelectionMode(JFileChooser.FILES_ONLY);
        } else {
            fc.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
        }
        returnVal = fc.showOpenDialog(this);
        if (returnVal == JFileChooser.APPROVE_OPTION) {
            File file = fc.getSelectedFile();
            sourceTF.setText(file.getAbsolutePath());
        }
        clearMessage();
    }//GEN-LAST:event_sourceTFMouseClicked

    private void destinationTFMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_destinationTFMouseClicked
        fc.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
        int returnVal = fc.showOpenDialog(this);
        if (returnVal == JFileChooser.APPROVE_OPTION) {
            File file = fc.getSelectedFile();
            destinationTF.setText(file.getAbsolutePath());
        }
        clearMessage();
    }//GEN-LAST:event_destinationTFMouseClicked

    private void addBActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_addBActionPerformed
        String action = actionCB.getSelectedItem().toString();
        if (action.equals(RecieverName.READ_FODER.getValue())) {
            readFolder(action);
        } else if (action.equals(RecieverName.EXECUTE_SCRIPT.getValue())) {
            executeScript(action);
        } else if (action.equals(RecieverName.SAVE_FILES.getValue())) {
            saveFiles(action);
        } else if (action.equals(RecieverName.ENCRYPT.getValue())) {
            encrypt(action);
        } else if (action.equals(RecieverName.DECRYPT.getValue())) {
            decrypt(action);
        } else if (action.equals(RecieverName.DECOMPRESS.getValue())) {
            decompress(action);
        } else if (action.equals(RecieverName.COMPRESS.getValue())) {
            compress(action);
        } else if (action.equals(SELECT_AN_OPTION)) {
            messageL.setText("seleccione una opcion");
            return;
        } else {
            actionsL.add(action);
            commands.add((Command) actionCB.getSelectedItem());
        }
        clearMessage();
        clearPaths();
    }//GEN-LAST:event_addBActionPerformed

    private void saveWorkFlowBActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_saveWorkFlowBActionPerformed
        if (nameTF.getText().isEmpty()) {
            this.setMessage("Ingrese un nombre");
            return;
        }
        if (actionsL.getItemCount() == 0) {
            this.setMessage("agregue tareas al flujo de trabajo");
            return;
        }
        WorkFlow workFlow = null;
        if (sequencialRB.isSelected()) {
            try {
                workFlow = new WorkFlow(null, null, commands, RunMode.SEQUENTIAL, 0,nameTF.getText());
            } catch (Exception ex) {
                setMessage(ex.getMessage());
                return;
            }
        } else if (parallelRB.isSelected()) {
            try {
                int command = (Integer)mainCommandSP.getValue() - 1;
                workFlow = new WorkFlow(null, null, commands, RunMode.PARALLEL, command,nameTF.getText());
            } catch (Exception ex) {
                setMessage(ex.getMessage());
                return;
            }
        } else {
            setMessage("seleccione un modo de ejecucion");
            return;
        }
        workFlows.add(workFlow);
        workFlowsCB.addItem(workFlow);
        actionCB.addItem(workFlow);
        actionsL.removeAll();
        commands = new ArrayList<Command>();
    }//GEN-LAST:event_saveWorkFlowBActionPerformed

    private void removeBActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_removeBActionPerformed
        int index = actionsL.getSelectedIndex();
        if (index == -1) {
            messageL.setText("seleccione una tarea");
        } else {
            commands.remove(index);
            actionsL.remove(index);
            clearMessage();
        }
    }//GEN-LAST:event_removeBActionPerformed

    private void nameTFMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_nameTFMouseClicked
        this.clearMessage();
    }//GEN-LAST:event_nameTFMouseClicked

    private void upBActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_upBActionPerformed
        int index = actionsL.getSelectedIndex();
        if (index == -1) {
            setMessage("seleccione una tarea");
        } else {
            if (index - 1 < 0) {
                return;
            }
            String item = actionsL.getItem(index);
            actionsL.remove(index);
            Command command = commands.remove(index);
            actionsL.add(item, index - 1);
            commands.add(index - 1, command);
        }
    }//GEN-LAST:event_upBActionPerformed

    private void downBActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_downBActionPerformed
        int index = actionsL.getSelectedIndex();
        if (index == -1) {
            setMessage("seleccione una tarea");
        } else {
            if (index == actionsL.getItemCount() - 1) {
                return;
            }
            String item = actionsL.getItem(index);
            actionsL.remove(index);
            Command command = commands.remove(index);
            actionsL.add(item, index + 1);
            commands.add(index + 1, command);
        }
    }//GEN-LAST:event_downBActionPerformed

    private void executeBActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_executeBActionPerformed
        WorkFlow workFlow = (WorkFlow) workFlowsCB.getSelectedItem();
        if (workFlow == null) {
            setMessage("Cree un flujo");
            return;
        }
        try {
            String readyToRun = workFlow.readyToRun();
            if (readyToRun.equals("Listo")) {
                workFlow.run();
                messageL.setText("Se ejecutaron las tareas correctamente");
                System.out.println("Se ejecutaron las tareas correctamente");
            } else {
                setMessage(readyToRun);
            }
            
        } catch (Exception e) {
            setMessage("Error al ejecutar las tareas");
            e.printStackTrace();
        }
    }//GEN-LAST:event_executeBActionPerformed

    private void parallelRBActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_parallelRBActionPerformed
        mainCommandL.setVisible(true);
        mainCommandSP.setVisible(true);
        mainCommandSP.setValue(1);
    }//GEN-LAST:event_parallelRBActionPerformed

    private void sequencialRBActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_sequencialRBActionPerformed
        mainCommandL.setVisible(false);
        mainCommandSP.setVisible(false);
    }//GEN-LAST:event_sequencialRBActionPerformed

    private void mainCommandSPStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_mainCommandSPStateChanged
        Integer value = (Integer) mainCommandSP.getValue();
        int size = commands.size();
        if (value > size) {
            if (size == 0) {
                mainCommandSP.setValue(1);
            } else {
                mainCommandSP.setValue(size);
            }
        }
    }//GEN-LAST:event_mainCommandSPStateChanged

    /**
     * @param args the command line arguments
     */
    public static void main(String args[]) {
        /* Set the Nimbus look and feel */
        //<editor-fold defaultstate="collapsed" desc=" Look and feel setting code (optional) ">
        /* If Nimbus (introduced in Java SE 6) is not available, stay with the default look and feel.
         * For details see http://download.oracle.com/javase/tutorial/uiswing/lookandfeel/plaf.html 
         */
        try {
            for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) {
                if ("Nimbus".equals(info.getName())) {
                    javax.swing.UIManager.setLookAndFeel(info.getClassName());
                    break;
                }
            }
        } catch (ClassNotFoundException ex) {
            java.util.logging.Logger.getLogger(MotorCommand.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (InstantiationException ex) {
            java.util.logging.Logger.getLogger(MotorCommand.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (IllegalAccessException ex) {
            java.util.logging.Logger.getLogger(MotorCommand.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (javax.swing.UnsupportedLookAndFeelException ex) {
            java.util.logging.Logger.getLogger(MotorCommand.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        }
        //</editor-fold>

        /* Create and display the form */
        java.awt.EventQueue.invokeLater(new Runnable() {
            public void run() {
                new MotorCommand().setVisible(true);
            }
        });
    }
    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JComboBox actionCB;
    private java.awt.List actionsL;
    private javax.swing.JButton addB;
    private javax.swing.JTextField destinationTF;
    private javax.swing.JButton downB;
    private javax.swing.JButton executeB;
    private javax.swing.JLabel jLabel1;
    private javax.swing.JLabel jLabel2;
    private javax.swing.JLabel jLabel3;
    private javax.swing.JLabel jLabel4;
    private javax.swing.JLabel jLabel5;
    private javax.swing.JLabel jLabel6;
    private javax.swing.JSeparator jSeparator1;
    private javax.swing.JSeparator jSeparator2;
    private javax.swing.JLabel mainCommandL;
    private javax.swing.JSpinner mainCommandSP;
    private javax.swing.JLabel messageL;
    private javax.swing.JTextField nameTF;
    private javax.swing.JRadioButton parallelRB;
    private javax.swing.JButton removeB;
    private javax.swing.ButtonGroup runModeBG;
    private javax.swing.JButton saveWorkFlowB;
    private javax.swing.JRadioButton sequencialRB;
    private javax.swing.JTextField sourceTF;
    private javax.swing.JButton upB;
    private javax.swing.JComboBox workFlowsCB;
    // End of variables declaration//GEN-END:variables

    private void init() {
        commands = new ArrayList<Command>();
        workFlows = new ArrayList<WorkFlow>();
        fc = new JFileChooser();
        fc.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
        RecieverName[] rns = RecieverName.values();
        actionCB.addItem(SELECT_AN_OPTION);
        for (RecieverName command : rns) {
            actionCB.addItem(command.getValue());
        }
        runModeBG.add(sequencialRB);
        runModeBG.add(parallelRB);
        sourceTF.setVisible(false);
        destinationTF.setVisible(false);
        mainCommandL.setVisible(false);
        mainCommandSP.setVisible(false);
    }

    private void setMessage(String message) {
        messageL.setText(message);
    }

    private void clearMessage() {
        this.setMessage("");
    }

    private void clearPaths() {
        sourceTF.setText("");
        destinationTF.setText("");
    }
    
    private void executeScript(String action){
        String source = sourceTF.getText();
        if (!FileControl.validatePath(source)) {
            setMessage("ruta de origen invalida, elija un archivo");
            return;
        }
        ExecuteSH executeSH = new ExecuteSH(source);
        Command command = new ConcreteCommand(executeSH);
        commands.add(command);
        actionsL.add(action);
    }
    
    private void encrypt(String action){
        Encrypt encrypt = new Encrypt(null, null);
        Command command = new ConcreteCommand(encrypt);
        commands.add(command);
        actionsL.add(action);
    }
    
    private void decrypt(String action){
        Decrypt decrypt = new Decrypt(null, null);
        Command command = new ConcreteCommand(decrypt);
        commands.add(command);
        actionsL.add(action);
    }

    private void readFolder(String action) {
        try {
            String source = sourceTF.getText();
            if (!FileControl.validatePath(source)) {
                setMessage("ruta de origen invalida, elija una carpeta");
                return;
            }
            ReadFolder readFolder = new ReadFolder(source);
            Command command = new ConcreteCommand(readFolder);
            commands.add(command);
            actionsL.add(action);
        } catch (Exception ex) {
            setMessage(ex.getMessage());
        }
    }

    private void saveFiles(String action) {
        try {
            String destination = destinationTF.getText();
            if (!FileControl.validatePath(destination)) {
                setMessage("ruta de destino invalida, elija una carpeta");
                return;
            }
            SaveFiles saveFiles = new SaveFiles(destination);
            Command command = new ConcreteCommand(saveFiles);
            commands.add(command);
            actionsL.add(action);
        } catch (Exception ex) {
            setMessage(ex.getMessage());
        }
    }

    private void decompress(String action) {
        try {
            Command command = new ConcreteCommand(RecieverFactory.getReciever(RecieverName.DECOMPRESS, null));
            commands.add(command);
            actionsL.add(action);
        } catch (Exception ex) {
            // Never Reach this
        }
    }

    private void compress(String action) {
        Zip zip = new Zip();
        Command command = new ConcreteCommand(zip);
        commands.add(command);
        actionsL.add(action);
    }
}
